﻿#include <stdexcept>
#include "JZCameraManager.h"
#include "JZNodeUtils.h"
#include "JZNodeEngine.h"
#include "JZNodeBind.h"
#include "JZCameraUVC.h"
#include "../JZModuleConfigFactory.h"

//JZCameraManagerConfig
QDataStream& operator<<(QDataStream& s, const JZCameraConfigEnum& param)
{
    JZModuleConfigFactory<JZCameraConfig>::instance()->saveToStream(s, param);
    return s;
}

QDataStream& operator>>(QDataStream& s, JZCameraConfigEnum& param)
{
    JZModuleConfigFactory<JZCameraConfig>::instance()->loadFromStream(s, param);
    return s;
}

int JZCameraManagerConfig::indexOfCamera(QString name) const
{
    for (int i = 0; i < cameraList.size(); i++)
    {
        auto config = cameraList[i].data();
        if (config->name == name)
            return i;
    }
    return -1;
}

QDataStream &operator<<(QDataStream &s, const JZCameraManagerConfig &param)
{
    s << param.cameraList;
    return s;
}
QDataStream &operator>>(QDataStream &s, JZCameraManagerConfig &param)
{
    s >> param.cameraList;
    return s;
}

//JZCameraManager
JZCameraManager::JZCameraManager(QObject* parent)
    :QObject(parent)
{
}

JZCameraManager::~JZCameraManager()
{    
    for(int i = 0; i < m_cameras.size(); i++)
    {
        m_cameras[i]->close();
        delete m_cameras[i];
    }
    m_cameras.clear();
}

void JZCameraManager::init()
{
    //clear
    for (int i = 0; i < m_cameras.size(); i++)
    {
        m_cameras[i]->stop();
        m_cameras[i]->close();
        m_cameras[i]->deleteLater();
    }
    m_cameras.clear();

    //init
    for (int i = 0; i < m_config.cameraList.size(); i++)
    {
        JZCamera *camera = createCamera(m_config.cameraList[i]);
        m_cameras.push_back(camera);
    }
}

void JZCameraManager::setConfig(const JZCameraManagerConfig &config)
{    
    m_config = config;
    init();    
}

const JZCameraManagerConfig &JZCameraManager::config() const
{
    return m_config;
}

void JZCameraManager::onFrameReady(cv::Mat mat)
{
    JZCamera* camera = qobject_cast<JZCamera*>(sender());
    emit sigFrameReady(camera->objectName(), mat);
}

void JZCameraManager::onError(QString error)
{
    JZCamera* camera = qobject_cast<JZCamera*>(sender());
    emit sigError(camera->objectName(), error);
}

void JZCameraManager::addCamera(const JZCameraConfigEnum &config)
{    
    Q_ASSERT(m_config.indexOfCamera(config->name) == -1);
    m_config.cameraList.push_back(config);
    m_cameras.push_back(createCamera(config));
}

void JZCameraManager::removeCamera(QString name)
{
    int idx = m_config.indexOfCamera(name);
    m_config.cameraList.removeAt(idx);
    auto camera = m_cameras[idx];
    m_cameras.removeAt(idx);
    if (camera->type() != Camera_Rtsp)
        delete camera;
    else
    {
        JZCameraRtsp *camera_rtsp = dynamic_cast<JZCameraRtsp*>(camera);
        camera_rtsp->stopAndDelete();
    }
}

bool JZCameraManager::open(QString name)
{
    auto c = camera(name);
    if (!c)
        return false;

    return c->open();
}

bool JZCameraManager::close(QString name)
{
    auto c = camera(name);
    if (!c)
        return false;

    c->close();
    return true;
}

bool JZCameraManager::start(QString name)
{
    auto c = camera(name);
    if (!c)
        return false;

    c->start();
    return true;
}

bool JZCameraManager::startOnce(QString name)
{
    auto c = camera(name);
    if (!c)
        return false;

    c->startOnce();
    return true;
}

bool JZCameraManager::stop(QString name)
{
    auto c = camera(name);
    if (!c)
        return false;

    c->stop();
    return true;
}

void JZCameraManager::stopAll()
{
    for (int i = 0; i < m_cameras.size(); i++)
        m_cameras[i]->stop();
}

bool JZCameraManager::setCamera(QString name, const JZCameraConfigEnum &config)
{    
    int cam_idx = m_config.indexOfCamera(name);
    if (cam_idx == -1)
        return false;

    JZCamera *c = nullptr;
    if (m_config.cameraList[cam_idx]->type != config->type)
    {
        m_cameras[cam_idx]->close();
        m_cameras[cam_idx]->deleteLater();
        m_cameras[cam_idx] = createCamera(config);
    }
    m_config.cameraList[cam_idx] = config;
    c = m_cameras[cam_idx];
    if (c->objectName() != config->name)
        c->setObjectName(config->name);
    return c->setConfig(config);    
}

QList<JZCamera*> JZCameraManager::cameraList()
{    
    return m_cameras;
}

JZCamera* JZCameraManager::camera(QString name)
{
    int idx = m_config.indexOfCamera(name);
    if (idx == -1)
        return NULL;

    return m_cameras[idx];
}

int JZCameraManager::indexOfCamera(QString name)
{
    return m_config.indexOfCamera(name);
}

JZCamera* JZCameraManager::createCamera(const JZCameraConfigEnum &config)
{
    bool open_ret = false;

    JZCamera *camera = nullptr;
    if(config->type == Camera_File)
    {
        JZCamera *camera_file = new JZCameraFile();
        camera = camera_file;
    }
    else if(config->type == Camera_UVC)
    {
        JZCameraUVC* camera_uvc = new JZCameraUVC();
        camera = camera_uvc;
    }
    else if(config->type == Camera_Hik)
    {
        JZCameraHik *camera_hik = new JZCameraHik();
        camera = camera_hik;
    }
    else if (config->type == Camera_Rtsp)
    {
        JZCameraRtsp *camera_rtsp = new JZCameraRtsp();
        camera = camera_rtsp;
    }
    else
    {
        Q_ASSERT(0);
    }
    connect(camera,&JZCamera::sigFrameReady,this,&JZCameraManager::onFrameReady);
    connect(camera,&JZCamera::sigError, this, &JZCameraManager::onError);
    
    camera->setObjectName(config->name);
    camera->setConfig(config);
    camera->setParent(this);
    return camera;
}

JZCamera* JZCameraGet(JZCameraManager* inst, QString name)
{
    JZCamera* camera = inst->camera(name);
    if (!camera)
        throw std::runtime_error("no camera");

    return camera;
}

//JZCameraInit
void JZCameraInit(JZCameraManager* inst, const QByteArray& buffer)
{
    JZCameraManagerConfig config = JZNodeUtils::fromBuffer<JZCameraManagerConfig>(buffer);
    inst->setConfig(config);
}

void JZCameraConnect(QObject * object, JZCameraManager *inst,QString name, JZFunctionPointer func)
{    
    inst->connect(inst, &JZCameraManager::sigInitFinish, object, [=]{
        JZCamera* camera = JZCameraGet(inst,name);

        camera->connect(camera, &JZCamera::sigFrameReady, object, [object, func](cv::Mat mat)
        {
            JZNodeObject* jzobj = qobjectToJZObject(object);
            QVariantList in;
            jzbind::createSlotParams<int>(in, mat);
            jzobj->onSigTrigger(func.functionName(),in);
        });
    });
}

void JZCameraStart(JZCameraManager* inst, QString name)
{
    JZCamera* camera = JZCameraGet(inst, name);
    if (!camera->isOpen())
    {
        if (!camera->open())
            throw std::runtime_error("open camera failed");
    }

    camera->start();
}

void JZCameraStartOnce(JZCameraManager* inst, QString name)
{    
    JZCamera* camera = JZCameraGet(inst, name);
    if (!camera->isOpen())
    {
        if(!camera->open())
            throw std::runtime_error("open camera failed");
    }

    camera->startOnce();
}

void JZCameraStop(JZCameraManager* inst, QString name)
{
    JZCamera* camera = JZCameraGet(inst, name);
    camera->stop();
}